#include "NodoInterno.h"
#include <algorithm>
using namespace Arboles;

NodoInterno::NodoInterno(unsigned int capacidadMinima, unsigned int capacidadMaxima) :
	Nodo(capacidadMinima, capacidadMaxima) {

}

/* Retorna el número del próximo hijo a leer de acuerdo a la clave pasada por parámetro
 * */
unsigned int NodoInterno::getNumeroNodoHijoALeer(string clave) {

	// pregunta si la clave es menor, si lo es le pasa la referencia izquierda
	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (clave < this->getRegistros()->at(i)->getClave())
			return this->getRegistros()->at(i)->getReferenciaNodoIzquierdo();
	}

	// si ninguna de las claves almacenadas es menor, devuelve la referencia derecha del último
	return this->getRegistros()->at(this->getRegistros()->size() - 1)->getReferenciaNodoDerecho();
}

/* Retorna el número de nodo izquierdo del nodo pasado por parámetro
 * Si no tiene devuelve 0  */
unsigned int NodoInterno::getHermanoIzquierdoDe(unsigned int numeroDeNodo) {

	if (numeroDeNodo == this->getRegistros()->at(0)->getReferenciaNodoIzquierdo())
		return 0;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {

		if (numeroDeNodo == this->getRegistros()->at(i)->getReferenciaNodoDerecho())
			return this->getRegistros()->at(i)->getReferenciaNodoIzquierdo();
	}

	return 0;
}

/* Retorna el número de nodo derecho del nodo pasado por parámetro
 * Si no tiene devuelve 0  */
unsigned int NodoInterno::getHermanoDerechoDe(unsigned int numeroDeNodo) {

	if (numeroDeNodo == this->getRegistros()->at(this->getRegistros()->size() - 1)->getReferenciaNodoIzquierdo())
		return 0;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {

		if (numeroDeNodo == this->getRegistros()->at(i)->getReferenciaNodoIzquierdo())
			return this->getRegistros()->at(i)->getReferenciaNodoDerecho();
	}

	return 0;
}

/* Actualiza la clave del registro con el numero de nodo como referencia derecha */
Utils::ResultadoOperacionArbol NodoInterno::actualizarClaveDe(unsigned int numeroDeNodo, string clave) {

	Utils::ResultadoOperacionArbol resultado = Utils::OK;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (this->getRegistros()->at(i)->getReferenciaNodoDerecho() == numeroDeNodo) {
			this->getRegistros()->at(i)->setClave(clave);
			resultado = Utils::ACTUALIZADO;
			break;
		}
	}

	return resultado;
}

bool sortFunctionRegistrosInternos(RegistroArbol* i, RegistroArbol* j) {
	return (i->getClave() < j->getClave());
}
/* Agrega un registro con la clave que nos pasan por parámetro, seteando sus referencias izq y derecha (y las de sus hnos)
 * En el caso de que la clave exista actualiza las referencias.
 * Retorna el resultado de la operación (por si hubo overflow) */
Utils::ResultadoOperacionArbol NodoInterno::agregar(RegistroArbol* registro) {

	bool existeLaClave = false;

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		if (this->getRegistros()->at(i)->getClave() == registro->getClave()) {

			this->getRegistros()->at(i)->setReferenciaNodoIzquierdo(registro->getReferenciaNodoIzquierdo());
			this->getRegistros()->at(i)->setReferenciaNodoDerecho(registro->getReferenciaNodoDerecho());

			// actualizo a los hermanos
			if (i > 0)
				this->getRegistros()->at(i - 1)->setReferenciaNodoDerecho(registro->getReferenciaNodoIzquierdo());

			if (i + 1 < this->getRegistros()->size())
				this->getRegistros()->at(i + 1)->setReferenciaNodoIzquierdo(registro->getReferenciaNodoDerecho());

			existeLaClave = true;
			delete registro;
			break;
		}
	}

	if (!existeLaClave) {
		this->getRegistros()->push_back(registro);
		sort(getRegistros()->begin(), getRegistros()->end(), sortFunctionRegistrosInternos);

		// lo encuentro y actualizo a sus hermanos
		for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
			if (this->getRegistros()->at(i)->getClave() == registro->getClave()) {

				if (i > 0)
					this->getRegistros()->at(i - 1)->setReferenciaNodoDerecho(registro->getReferenciaNodoIzquierdo());

				if (i + 1 < this->getRegistros()->size())
					this->getRegistros()->at(i + 1)->setReferenciaNodoIzquierdo(registro->getReferenciaNodoDerecho());

				break;
			}
		}
	}

	if (this->getCapacidadMaxima() < this->getTamanioDelBuffer())
		return Utils::SOBREFLUJO;
	else
		return Utils::ACTUALIZADO;
}

/* Remueve el registro de acuerdo al número de nodo que nos pasan como referencia izquierda del registro.
 * Si el nodo es la referencia izq, del 'registro a remover' lo remuevo y no hago nada más.
 * Si el nodo es la referencia der, actualizo la referencia izquierda izq del siguiente (si tiene siguiente) con la ref
 * izquierda del 'registro a remover', y luego lo remuevo.
 * Retorna el resultado de la operación (por si hubo underflow) */
Utils::ResultadoOperacionArbol NodoInterno::remover(RegistroArbol* registro) {

	unsigned int claveARemover = registro->getReferenciaNodoIzquierdo();

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {

		if (this->getRegistros()->at(i)->getReferenciaNodoIzquierdo() == claveARemover) {
			delete this->getRegistros()->at(i);
			this->getRegistros()->erase(this->getRegistros()->begin() + i);
			break;
		} else {
			if (this->getRegistros()->at(i)->getReferenciaNodoDerecho() == claveARemover) {
				if (i + 1 < this->getRegistros()->size())
					this->getRegistros()->at(i + 1)->setReferenciaNodoIzquierdo(
							this->getRegistros()->at(i)->getReferenciaNodoIzquierdo());

				delete this->getRegistros()->at(i);
				this->getRegistros()->erase(this->getRegistros()->begin() + i);
				break;
			}
		}
	}

	if (this->getCapacidadMinima() >= (this->getTamanioDelBuffer() - 3 * sizeof(int)))
		return Utils::SUBFLUJO;
	else
		return Utils::ACTUALIZADO;
}

unsigned int NodoInterno::getTamanioDelBuffer() {
	// Nivel + Cant.Registros Internos
	int tamanioDelBuffer = 2 * sizeof(int);

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		tamanioDelBuffer += 3 * sizeof(int); // Tamanio Clave Registro + Referencia Derecha + Referencia Izquierda
		tamanioDelBuffer += this->getRegistros()->at(i)->getClave().size(); //Longitud de la clave del registro
	}

	return tamanioDelBuffer;
}

void NodoInterno::hidratarse(char* buffer, unsigned int tamanioDelBuffer) {

	//escribo el buffer en el stringStream
	stringstream* ss = new stringstream(stringstream::in | stringstream::out | stringstream::binary);
	ss->seekg(0, ios::beg);
	ss->write(buffer, tamanioDelBuffer);

	//int Nivel
	int nivel= 0;
	ss->read((char*) &(nivel), sizeof(int));
	this->setNivel(nivel);

	//int Cantidad Registros
	unsigned int cantidadRegistros= 0;
	ss->read((char*) &(cantidadRegistros), sizeof(int));

	//Registro por Registro del nodo
	unsigned int refIzquierda= 0;
	unsigned int refDerecha= 0;
	for (unsigned int i = 0; i < cantidadRegistros; i++) {

		RegistroArbol* registro = new RegistroArbol(Utils::ObtenerSiguienteStringVariable(ss));

		//Referencia Izquierda
		ss->read((char*) &(refIzquierda), sizeof(int));
		registro->setReferenciaNodoIzquierdo(refIzquierda);

		//Referencia Derecha
		ss->read((char*) &(refDerecha), sizeof(int));
		registro->setReferenciaNodoDerecho(refDerecha);

		this->getRegistros()->push_back(registro);
	}

	delete ss;

}

char* NodoInterno::construirBuffer() {

	//agrupo los atributos
	stringstream* ss = new stringstream(stringstream::in | stringstream::out | stringstream::binary);
	ss->seekp(0, ios::end);

	//Nivel
	int nivel = this->getNivel();
	ss->write((char*) &(nivel), sizeof(int));

	//Cantidad de Registros
	unsigned int cantidadDeRegistros = this->getRegistros()->size();
	ss->write((char*) &cantidadDeRegistros, sizeof(int));

	//Registro por Registro del nodo
	unsigned int refIzquierda= 0;
	unsigned int refDerecha= 0;
	for (unsigned int i = 0; i < cantidadDeRegistros; i++) {

		//Tamanio y Clave del Registro
		string claveRegistro = this->getRegistros()->at(i)->getClave();
		unsigned int tamanioClave = claveRegistro.size();
		ss->write((char*) &tamanioClave, sizeof(int));
		ss->write(claveRegistro.c_str(), tamanioClave);

		//Referencia Izquierda del Registro
		refIzquierda = this->getRegistros()->at(i)->getReferenciaNodoIzquierdo();
		ss->write((char*) &refIzquierda, sizeof(int));

		//Referencia Derecha del Registro
		refDerecha = this->getRegistros()->at(i)->getReferenciaNodoDerecho();
		ss->write((char*) &refDerecha, sizeof(int));

	}

	//construyo el buffer
	unsigned int tamanioDelBuffer = this->getTamanioDelBuffer();
	char* buffer = new char[tamanioDelBuffer];
	ss->read(buffer, tamanioDelBuffer);
	delete ss;

	//lo devuelvo
	return buffer;
}

NodoInterno::~NodoInterno() {

	for (unsigned int i = 0; i < this->getRegistros()->size(); i++) {
		delete this->getRegistros()->at(i);
	}

	delete this->getRegistros();
}
